package com.skyyan.netty.jjf.protocol;

import cn.hutool.core.codec.BCD;
import cn.hutool.core.util.*;
import com.skyyan.netty.jjf.util.CardNumberUtil;
import com.skyyan.netty.jjf.util.ChineseUtils;
import com.skyyan.netty.jjf.util.IntThreeBytesUtil;
import com.skyyan.netty.jjf.util.JJFByteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;


/**
 * C.6 余额查询
 * AFN = 0x98
 *
 * 上行报文格式（设备 → 中心）：
 * 起始字符0x68
 * 长度1个字节
 * 起始字符0x68
 * - 控制域C：0x01   上行
 * - 地址域：19个字节
 *   地址域[0]-[15]  设备注册号 16进制,低位在前 高位在后
 *   地址域[16]-[18]  充值管理机序列号 16进制,低位在前 高位在后
 * - AFN(1字节)：0x98
 * - 数据域16个字节
 *   数据域[0]-[15]   农户注册号 16进制,低位在前 高位在后
 * - CRC8校验码(1字节)
 * -结束字符 0x16
 *
 * 下行报文格式（中心 →设备） :
 * 起始字符0x68
 * 长度1个字节
 * 起始字符0x68
 * - 控制域C：0x00   下行
 * - AFN(1字节)：0x98
 * - 数据域 22个字节
 *   数据域[0]    1个字节 BCD码；00启用，01禁用 02隶属信息不符 03无次卡信息 ,04其他
 *   数据域[1]-[4] 余额 4字节 16进制 低位在前 高位在后，单位为分
 *   数据域[5]-[16] 12个字节 姓名 16进制UTF-8编码，每三个字节一个中文
 *   数据域[17]-[21] 5个字节 卡号 16进制 高位在前 低位在后
 * - CRC8校验码(1字节)
 * -结束字符 0x16
 * @author skyyan
 */
public class BalanceQueryMessage extends Message {

    private static final Logger log = LoggerFactory.getLogger(BalanceQueryMessage.class);

    /**
     * 上行余额查询报文（设备 → 中心）
     * 需要参数：设备注册号、充值管理机序列号、农户注册号
     */
    public static class Uplink extends BalanceQueryMessage {
        // 设备注册号 (16字节，低位在前)
        private byte[] deviceRegIdBytes;


        // 设备序列号 (3字节，低位在前)
        private byte[] deviceSerialNoBytes;

        //充值管理机 序列号 占三个字节
        private int deviceSerialNo;
        // 农户注册号 (16字节，低位在前)
        private byte[] farmerRegIdBytes;

        /**
         * 构造一个上行的余额查询报文
         * @param address 地址域 (19字节)
         * @param farmerRegistrationId 农户注册编号 (16字节)
         */
        public Uplink(byte[] address, byte[] farmerRegistrationId) {
            super(FrameConstants.CONTROL_UPLINK, address,FrameConstants.AFN_BALANCE_QUERY,
                  farmerRegistrationId != null ? farmerRegistrationId.clone() : new byte[16]);

            // 从地址域中提取设备注册号和设备序列号
            if (address != null && address.length == 19) {
                this.deviceRegIdBytes = new byte[16];
                this.deviceSerialNoBytes = new byte[3];
                this.deviceSerialNo= IntThreeBytesUtil.threeBytesLittleEndianToInt(this.deviceSerialNoBytes);
                System.arraycopy(address, 0, this.deviceRegIdBytes, 0, 16);
                System.arraycopy(address, 16, this.deviceSerialNoBytes, 0, 3);
            }
            this.farmerRegIdBytes = farmerRegistrationId != null ? farmerRegistrationId.clone() : new byte[16];
        }

        /**
         * 构造一个上行的余额查询报文
         * @param deviceRegId 设备注册号 (16字节)
         * @param deviceSerialNo 充值管理机序列号 (int类型) 3字节
         * @param farmerRegistrationId 农户注册编号 (16字节)
         */
        public Uplink(byte[] deviceRegId, int deviceSerialNo, byte[] farmerRegistrationId) {
            super(FrameConstants.CONTROL_UPLINK,
                  createAddress(deviceRegId, deviceSerialNo),
                  farmerRegistrationId != null ? farmerRegistrationId.clone() : new byte[16]);

            // 保存设备注册号和充值管理机序列号
            this.deviceRegIdBytes = deviceRegId != null ? deviceRegId.clone() : new byte[16];
            this.deviceSerialNoBytes=IntThreeBytesUtil.intTo3BytesLittleEndian(deviceSerialNo);
            this.deviceSerialNo=deviceSerialNo;
            this.farmerRegIdBytes = farmerRegistrationId != null ? farmerRegistrationId.clone() : new byte[16];
        }

        /**
         * 创建地址域
         * @param deviceRegId 设备注册号 (16字节)
         * @param deviceSerialNo 充值管理机序列号 (int类型)
         * @return 19字节地址域
         */
        private static byte[] createAddress(byte[] deviceRegId, int deviceSerialNo) {
            byte[] address = new byte[19];

            // 设备注册号 (16字节，低位在前)
            if (deviceRegId != null) {
                System.arraycopy(deviceRegId, 0, address, 0, Math.min(deviceRegId.length, 16));
            }

            // 充值管理机序列号 (3字节，低位在前)
            address[16] = (byte) (deviceSerialNo & 0xFF);
            address[17] = (byte) ((deviceSerialNo >> 8) & 0xFF);
            address[18] = (byte) ((deviceSerialNo >> 16) & 0xFF);

            return address;
        }


        /**
         * 获取设备序列号 (int类型)
         * @return 设备序列号
         */
        public int getDeviceSerialNoInt() {
            if (deviceSerialNoBytes != null && deviceSerialNoBytes.length >= 3) {
                return (deviceSerialNoBytes[0] & 0xFF) |
                       ((deviceSerialNoBytes[1] & 0xFF) << 8) |
                       ((deviceSerialNoBytes[2] & 0xFF) << 16);
            }
            return 0;
        }

        /**
         * 获取设备注册号
         * @return 设备注册号 (16字节，低位在前)
         */
        public byte[] getDeviceRegId() {
            return deviceRegIdBytes != null ? deviceRegIdBytes.clone() : new byte[16];
        }

        /**
         * 获取设备序列号
         * @return 设备序列号 (3字节，低位在前)
         */
        public byte[] getDeviceSerialNo() {
            return deviceSerialNoBytes != null ? deviceSerialNoBytes.clone() : new byte[3];
        }

        /**
         * 获取农户注册号
         * @return 农户注册号 (16字节，低位在前)
         */
        public byte[] getFarmerRegId() {
            return farmerRegIdBytes != null ? farmerRegIdBytes.clone() : new byte[16];
        }

        @Override
        public String toString() {
            return "BalanceQueryMessage.Uplink{" +
                    "control=0x" + String.format("%02X", getControl()) +
                    ", afn=0x" + String.format("%02X", getAfn()) +
                    ", deviceRegId=" + bytesToHex(getDeviceRegId()) +
                    ", deviceSerialNo=" + bytesToHex(getDeviceSerialNo()) +
                    ", deviceSerialNoInt=0x" + String.format("%06X", getDeviceSerialNoInt()) +
                    ", farmerRegId=" + bytesToHex(getFarmerRegId()) +
                    '}';
        }
    }

    /**
     * 下行余额查询报文（中心 → 设备）
     * 需要参数：状态码、余额、姓名、卡号
     */
    public static class Downlink extends BalanceQueryMessage {
        // 状态 数据域[0] BCD码
        private byte statusCodeBCD;
        // 余额 数据域[1]-[4] 4字节 16进制 低位在前 高位在后，单位为分
        private byte[] moneyLSB;
        //余额
        private int moneyValue;
        /**
         * 姓名 数据域[5]-[16] 12个字节 姓名 16进制UTF-8编码，每三个字节一个中文
         * 一共12个字节对吧  就是说支持最多4个汉字的姓名，比如张三，那么用到6个字节，后面6个补0即可  比如王小二  那就前9个是 utf-8 后面3个补0
         */
        private byte[] usernameBytes;
        private String username;
        // 卡号 数据域[17]-[21] 5个字节 卡号 16进制 高位在前 低位在后 ;位数不足高位补0
        private byte[] numberNo;
        /**
         * 卡号
         */
        private String cardNO;

        public int getMoneyValue() {
            return moneyValue;
        }

        public void setMoneyValue(int moneyValue) {
            this.moneyValue = moneyValue;
        }

        /**
         * 构造一个下行的余额查询报文
         * @param statusCodeBCD 状态码
         * @param balance 余额(单位为分)
         * @param username 用户名
         * @param cardNo 卡号
         */
        public Downlink(byte statusCodeBCD, int balance, String username, String cardNo)throws Exception {
            super(FrameConstants.CONTROL_DOWNLINK, null, createUserData(statusCodeBCD, balance, username, cardNo));
            this.statusCodeBCD = statusCodeBCD;
            this.moneyLSB = new byte[4];
            byte[] balanceBytes = ByteUtil.intToBytes(balance); // 小端序
            System.arraycopy(balanceBytes, 0, this.moneyLSB, 0, 4);
            this.usernameBytes = username != null ? ChineseUtils.chineseToBytes(username) : new byte[12];
            this.username=username;
            this.numberNo=cardNo != null ? CardNumberUtil.decimalStringToBytes(cardNo) : new byte[5];
            this.cardNO=cardNo;
        }
        // 私有的辅助方法，用于将字节数组转换为十六进制字符串
        private static String bytesToHex(byte[] bytes) {
            if (bytes == null) return "null";
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                sb.append(String.format("%02X ", b & 0xFF));
            }
            return sb.toString().trim();
        }
        /**
         * 创建卡号字节数组（5字节，高位在前，低位在后）
         * @param cardNo 卡号字符串
         * @return 5字节卡号数组
         */
        private static byte[] createCardNoBytes(String cardNo) {
          return  CardNumberUtil.decimalStringToBytes(cardNo);
//            byte[] cardNoBytes = new byte[5];
//            if (cardNo != null) {
//                // 将卡号字符串转换为字节数组，高位在前 （US_ASCII编码可以确保我们得到预期的字节）
//                byte[] inputBytes = cardNo.getBytes(StandardCharsets.US_ASCII);
//                // 复制到卡号字节数组中，如果长度不足5字节，则高位补0
//                int copyLength = Math.min(inputBytes.length, 5);
//                System.arraycopy(inputBytes, 0, cardNoBytes, 5 - copyLength, copyLength);
//            }
//            return cardNoBytes;
        }

        /**
         * 供解码器使用的构造函数
         * @param address 地址域
         * @param userData 用户数据
         */
        public Downlink(byte[] address, byte[] userData) throws Exception{
            super(FrameConstants.CONTROL_DOWNLINK, address, userData);
            System.out.println("余额查询 userData长度="+userData.length);
            if (userData != null) {
                if (userData.length < 22) {
                    throw new IllegalArgumentException("解析查询余额下发数据长度小于22");
                }
                this.statusCodeBCD = userData[0]; // 数据域状态
                this.moneyLSB = Arrays.copyOfRange(userData, 1, 5); // 余额 数据域[1]-[4] 低位在前，高位在后
                this.usernameBytes = Arrays.copyOfRange(userData, 5, 17); // 姓名 数据域[5]-[16] 三个字节一个中文
                this.numberNo = Arrays.copyOfRange(userData, 17, 22); // 卡号 数据域[17]-[21] 高位在前 低位在后
                this.cardNO= CardNumberUtil.bytesToDecimalString(numberNo);
                this.username=ChineseUtils.byteToChinese(usernameBytes);
                this.moneyValue=ByteUtil.bytesToInt(moneyLSB);

            }
        }

        /**
         * 创建用户数据
         * @param statusCodeBCD 状态码
         * @param balance 余额(单位为分)
         * @param username 用户名
         * @param cardNo 卡号
         * @return 用户数据
         */
        private static byte[] createUserData(byte statusCodeBCD, int balance, String username, String cardNo) {
            byte[] userData = new byte[22];
            userData[0] = statusCodeBCD;

            // 余额 4字节 小端序
            byte[] balanceBytes = ByteUtil.intToBytes(balance);
            System.arraycopy(balanceBytes, 0, userData, 1, 4);
            byte[] nameBytes = new byte[12];;
            // 姓名 12字节 UTF-8编码
            if (username != null) {
                nameBytes = username.getBytes(StandardCharsets.UTF_8);

            }
            System.arraycopy(nameBytes, 0, userData, 5, Math.min(nameBytes.length, 12));
            byte[] cardNoBytes=new byte[5];
            // 卡号 5字节 ASCII编码，高位在前
            if (cardNo != null) {
                cardNoBytes=CardNumberUtil.decimalStringToBytes(cardNo);
            }
            System.arraycopy(cardNoBytes, 0, userData, 17, 5);
            return userData;
        }

        public byte getStatusCodeBCD() {
            return statusCodeBCD;
        }

        public void setStatusCodeBCD(byte statusCodeBCD) {
            this.statusCodeBCD = statusCodeBCD;
        }

        public byte[] getMoneyLSB() {
            return moneyLSB;
        }

        public void setMoneyLSB(byte[] moneyLSB) {
            this.moneyLSB = moneyLSB;
        }

        public byte[] getUsernameBytes() {
            return usernameBytes;
        }

        public void setUsernameBytes(byte[] usernameBytes) {
            this.usernameBytes = usernameBytes;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public byte[] getNumberNo() {
            return numberNo;
        }

        public void setNumberNo(byte[] numberNo) {
            this.numberNo = numberNo;
        }

        public String getCardNO() {
            return cardNO;
        }

        public void setCardNO(String cardNO) {
            this.cardNO = cardNO;
        }

        @Override
        public String toString() {
            byte[] address = getAddress();
            try {
                return "BalanceQueryMessage.Downlink{" +
                        "control=0x" + String.format("%02X", getControl()) +
                        ", afn=0x" + String.format("%02X", getAfn()) +
                        ", address=" + (address != null ? bytesToHex(address) : "null") +
                        ", statusCode=0x" + String.format("%02X", getStatusCodeBCD()) +
                        ", balance=" + getMoneyValue() +
                        ", username=" + getUsername() +
                        ", cardNo=" + getCardNO() +
                        ", cardNoHex=" + (this.numberNo != null ? bytesToHex(this.numberNo) : "null") +
                        '}';
            } catch (Exception e) {
                e.printStackTrace();
                return "BalanceQueryMessage.Downlink{" +
                        "control=0x" + String.format("%02X", getControl()) +
                        ", afn=0x" + String.format("%02X", getAfn()) +
                        ", address=" + (address != null ? bytesToHex(address) : "null") +
                        ", statusCode=0x" + String.format("%02X", getStatusCodeBCD()) +
                        ", balance=" + getMoneyValue() +
                        ", username=" + getUsername() +
                        ", cardNo=" + getCardNO() +
                        '}';
             }
        }
    }

    /**
     * 供内部使用的构造函数
     * @param control 控制域
     * @param address 地址域
     * @param afn AFN功能码
     * @param userData 用户数据
     */
    protected BalanceQueryMessage(byte control, byte[] address, byte afn, byte[] userData) {
        super(control, address, afn, userData);
    }

    /**
     * 供解码器使用的构造函数
     * @param control 控制域
     * @param address 地址域
     * @param userData 用户数据
     */
    public BalanceQueryMessage(byte control, byte[] address, byte[] userData) {
        super(control, address, FrameConstants.AFN_BALANCE_QUERY, userData);
    }

    /**
     * 将字节数组转换为十六进制字符串表示
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < bytes.length; i++) {
            if (i > 0) {
                sb.append(" ");
            }
            sb.append(String.format("%02X", bytes[i] & 0xFF));
        }
        sb.append("]");
        return sb.toString();
    }
}

